package io.github.wslxm.springbootplus2.config;

import io.github.wslxm.springbootplus2.core.config.error.ErrorException;
import io.github.wslxm.springbootplus2.core.result.Result;
import io.github.wslxm.springbootplus2.core.result.ResultType;
import io.github.wslxm.springbootplus2.core.utils.I18nUtil;
import io.github.wslxm.springbootplus2.utils.XjErrorMsgUtil;
import io.github.wslxm.springbootplus2.starter.redis.config.error.RedisErrorException;
import io.github.wslxm.springbootplus2.starter.robot.service.RobotService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.List;

/**
 * 异常处理类/ 全局异常 /自定义异常
 */
@SuppressWarnings("all")
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    @Autowired
    private HttpServletRequest request;
    @Autowired
    private HttpServletResponse response;
    @Autowired
    private RobotService robotService;


    /**
     * 全局异常|受检查异常 --> Exception 。可以理解为错误，必须要开发者解决以后才能编译通过，这里JSR 303 为受检查异常
     * 全局异常|运行时异常 --> RuntimeException extends Exception： 运行时异常,又称不受检查异常，不受检查！
     * 受检查异常解决的方法有两种1：throw到上层，2，try-catch处理。
     *
     * @param e
     * @return io.github.wslxm.admincore.platform.vo.ResponseData
     * @author ws
     * @mail 1720696548@qq.com
     * @date 2020/2/9 0009 10:06
     */
    @ExceptionHandler(Exception.class)
    public Result<Object> exceptionHandler(Exception e) {
        // 日志模板
        String logStr = "\n\r### [全局异常-GlobalExceptionHandler] --> URl: " + request.getRequestURL() + " --> error: ";
        // 错误详情
        String errorMsg = XjErrorMsgUtil.getErrorStr(e);
        // 保底返回
        Result result = Result.error(ResultType.SYS_ERROR_500, errorMsg);
        if (e instanceof ErrorException) {
            // 自定义异常类 ErrorException
            ErrorException error = (ErrorException) e;
            result = Result.error(error.getCode(), error.getMsg());
        } else if (e instanceof NoResourceFoundException) {
            NoResourceFoundException error = (NoResourceFoundException) e;
            result = Result.error(error.getStatusCode().value(), error.getMessage());
        } else if (e instanceof RedisErrorException) {
            /**
             * 自定义异常-> Redis 异常类 RedisErrorException
             */
            RedisErrorException error = (RedisErrorException) e;
            result = Result.error(error.getCode(), error.getMsg());
        } else if (e instanceof HttpMessageNotReadableException) {
            /**
             * 传递参数错误 - 枚举参数 | json参数错误, 请检查 json 是否完整，序列化失败（只打印核心错误内容）
             */
            HttpMessageNotReadableException error = (HttpMessageNotReadableException) e;
            log.error(logStr + errorMsg);
            result = Result.error(ResultType.PARAM_ERROR, errorMsg);
        } else if (e instanceof MethodArgumentNotValidException) {
            /**
             * body JSR 303 为参数验证错误（只打印核心错误内容）
             */
            List<FieldError> fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
            // 错误字段，多个错误，取第一个  // 错误 message，多个错误，取第一个
            String msg = fieldErrors.get(0).getField() + " " + fieldErrors.get(0).getDefaultMessage();
            result = Result.error(ResultType.PARAM_ERROR.getValue(), fieldErrors.get(0).getDefaultMessage());
        } else if (e instanceof BindException) {
            /**
             * query JSR 303 为参数验证错误（只打印核心错误内容）
             */
            List<FieldError> fieldErrors = ((BindException) e).getBindingResult().getFieldErrors();
            // 错误字段，多个错误，取第一个  // 错误 message，多个错误，取第一个
            String msg = fieldErrors.get(0).getField() + " " + fieldErrors.get(0).getDefaultMessage();
            result = Result.error(ResultType.PARAM_ERROR.getValue(), fieldErrors.get(0).getDefaultMessage());
        } else if (e instanceof MissingServletRequestParameterException) {
            /**
             * 未传递 Parameter 参数验证错误, 一般为 @Parameter 指定参数未传递（只打印核心错误内容）
             */
            String i18nMessage = I18nUtil.getMessage(ResultType.PARAM_MISSING.getMsg());
            String parameterName = ((MissingServletRequestParameterException) e).getParameterName();
            // String parameterType = ((MissingServletRequestParameterException) e).getParameterType();
            String msg = i18nMessage + " " + parameterName;
            // 返回前端提示name参数:类型 类型参数未传递
            result = Result.error(ResultType.PARAM_MISSING.getValue(), msg);
        } else if (e instanceof MethodArgumentTypeMismatchException) {
            /**
             * 方法参数类型不匹配 mvc
             */
            String i18nMessage = I18nUtil.getMessage(ResultType.PARAM_ERROR.getMsg());
            MethodParameter parameter = ((MethodArgumentTypeMismatchException) e).getParameter();
            String name = ((MethodArgumentTypeMismatchException) e).getName();
            Class<?> requiredType = ((MethodArgumentTypeMismatchException) e).getRequiredType();
            String msg = i18nMessage + " " + name + " " + requiredType.getSimpleName();
            result = Result.error(ResultType.PARAM_ERROR.getValue(), msg);
        } else if (e instanceof DataIntegrityViolationException) {
            /**
             * 保存到数据库DB相关错误
             */
            DataIntegrityViolationException e1 = (DataIntegrityViolationException) e;
            if (errorMsg.indexOf("doesn't have a default value") != -1) {
                /**
                 * 数据没有默认值
                 * Field 'name' doesn't have a default value; nested exception is java.sql.SQLException: Field 'name' doesn't have a default value
                 */
                // 获取到数据库错误字段name
                String i18nMessage = I18nUtil.getMessage(ResultType.DB_FIELD_MISSING.getMsg());
                int indexStart = errorMsg.indexOf("'");
                int indexEnd = errorMsg.indexOf("'", indexStart + 1);
                String name = errorMsg.substring(indexStart + 1, indexEnd);
                String msg = name + " " + i18nMessage;
                result = Result.error(ResultType.DB_FIELD_MISSING.getValue(), msg);
            } else if (errorMsg.indexOf("for key 'PRIMARY'") != -1) {
                /**
                 * 主键 ID 重复
                 * ### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'string' for key 'PRIMARY'
                 */
                result = Result.error(ResultType.DB_PRIMARY_REPEAT, errorMsg);
            } else if (errorMsg.indexOf("Data truncation: Data too long for column") != -1) {
                /**
                 * 字段内容超长
                 * ### com.btpk.example.manage.creative.mapper.CreativeArticleMapper.insert (batch index #1) failed. Cause: java.sql.BatchUpdateException: Data truncation: Data too long for column 'describe' at row 1
                 */
                String i18nMessage = I18nUtil.getMessage(ResultType.PARAM_FIELD_MAX_LEN.getMsg());
                // 获取到数据库错误字段 name
                int indexStart = errorMsg.indexOf("'");
                int indexEnd = errorMsg.indexOf("'", indexStart + 1);
                String name = errorMsg.substring(indexStart + 1, indexEnd);
                String msg = name + " " + i18nMessage;
                result = Result.error(ResultType.PARAM_FIELD_MAX_LEN.getValue(), msg);
            } else {
                /**
                 * 执行sql时的其他错误
                 */
                result = Result.error(ResultType.DB_EXECUTE_SQL_ERROR, errorMsg);
            }
        }
        log.error("{} code: {} msg: {} errorMsg: {}", logStr, result.getCode(), result.getMsg(), result.getErrorMsg());
        return result;
    }
}


